package main

import (
	"bufio"
	"fmt"
	"github.com/spf13/cobra"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
)

/**
 *主要目的简化plugin创建时候的繁琐国程,以及编译脚本的自动生存
 */

var rootCmd = &cobra.Command{
	Use:   "ph",
	Short: "A brief description of your application",
	Long:  `plugin 辅助工具`,
}

func init() {

	rootCmd.AddCommand(&cobra.Command{
		Use:   "add",
		Short: "添加新插件",
		Long:  `添加一个新的插件`,
		Run: func(cmd *cobra.Command, args []string) {

			log.Println("开始创建新的插件")
			//如果为传参不需要继续执行了
			if len(args) < 2 {
				log.Printf("%s [author] [pulginName]\n", cmd.Name())
				os.Exit(0)
			}

			dir := filepath.Dir(getCurrentAbPathByExecutable())
			log.Println("当前路径：", dir)
			path := fmt.Sprintf("%s%splugins-project%s%s%s%s%s",
				dir, string(os.PathSeparator), string(os.PathSeparator), args[0], string(os.PathSeparator), args[1], string(os.PathSeparator))
			log.Println("工作目录：", path)
			//创建目录
			if Exists(path) == false {
				Mkdir(path)
			} else {
				if !IsDir(path) {
					Mkdir(path)
				}
			}

			runFilePath := fmt.Sprintf("%s%s.go", path, args[1])
			log.Printf("即将创建文件：%s\n", runFilePath)
			//创建插件入口文件
			if Exists(runFilePath) && IsFile(runFilePath) {
				log.Printf("文件已存在：%s\n", runFilePath)
				log.Println("如需重新生成，请删除")
			}

			fileMsg := fmt.Sprintf("package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n)\n\nconst (\n\tpluginName    = \"%s@%s\"\n\tpluginVersion = 0x00010000\n)\n\nfunc init() {\n}\n\n// Load 不可以删除，否则会导致无法注册插件\nfunc Load(register func(name string, version uint64) error) error {\n\terr := register(pluginName, pluginVersion)\n\tif err != nil {\n\t\tlog.Println(err.Error())\n\t\treturn err\n\t}\n\tlog.Printf(\"loading %%s plugin\\n\", pluginName)\n\trun()\n\treturn nil\n}\n\n// Unload 插件卸载实现动作\nfunc Unload() error {\n\tfmt.Printf(\"unload %%s, version: 0x%%x\\n\", pluginName, pluginVersion)\n\treturn nil\n}\n\nfunc run() {\n\tgo func() {\n\n\t}()\n}\n", args[0], args[1])

			err := ioutil.WriteFile(runFilePath, []byte(fileMsg), 0644)

			//如果ID写入失败则直接报错，停止程序
			if err != nil {
				log.Fatal(err)
			}

		},
	})

	rootCmd.AddCommand(&cobra.Command{
		Use:   "build",
		Short: "编译插件",
		Long:  `编译插件`,
		Run: func(cmd *cobra.Command, args []string) {
			log.Println("开始编译打包插件")
			if len(args) < 2 {
				fmt.Printf("%s [author] [pulginName]\n", cmd.Name())
				os.Exit(0)
			}
			log.Printf("插件作者：%s\n", args[0])
			log.Printf("插件名称：%s\n", args[1])
			log.Printf("插件标识：%s@%s\n", args[0], args[1])
			log.Printf("插件文件：%s@%s.so\n", args[0], args[1])

			dir := filepath.Dir(getCurrentAbPathByExecutable())
			buildOutFile := dir + string(os.PathSeparator) + "plugins" + string(os.PathSeparator) + args[0] + "@" + args[1] + ".so"
			sourceFile := dir + string(os.PathSeparator) + "plugins-project" + string(os.PathSeparator) + args[0] + string(os.PathSeparator) + args[1] + string(os.PathSeparator) + args[1] + ".go"

			log.Printf("编译文件： %s 到 %s\n\n", sourceFile, buildOutFile)

			err := CmdAndChangeDirToShow(getCurrentAbPathByExecutable(), "go", []string{"build", "-buildmode=plugin", "-o", buildOutFile, sourceFile})
			if err != nil {
				fmt.Println(err)
				return
			}

		},
	})
}

func main() {

	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

// Exists 判断所给路径文件/文件夹是否存在
func Exists(path string) bool {
	_, err := os.Stat(path) //os.Stat获取文件信息
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}

// IsDir 判断所给路径是否为文件夹
func IsDir(path string) bool {
	s, err := os.Stat(path)
	if err != nil {
		return false
	}
	return s.IsDir()
}

// IsFile 判断所给路径是否为文件
func IsFile(path string) bool {
	return !IsDir(path)
}

func Mkdir(path string) bool {
	log.Printf("即将创建文件夹：%s\n", path)
	err := os.MkdirAll(path, os.ModePerm)
	if err != nil {
		fmt.Println("文件夹创建失败：", err)
		return false
	}
	return true
}

func ExecShell(myProgram string, cmdArguments []string) {
	fmt.Println(cmdArguments)
	cmd := exec.Command(myProgram, cmdArguments...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()

	fmt.Println(cmd.Stdout)
	if err != nil {
		fmt.Println(cmd.Stdout)
		fmt.Println(cmd.Stderr)
		fmt.Println(err)
	}
}

func CmdAndChangeDirToShow(dir string, commandName string, params []string) error {
	PATH := os.Getenv("PATH")
	err := os.Setenv("PATH", PATH)
	if err != nil {
		return err
	}
	cmd := exec.Command(commandName, params...)
	log.Printf("执行命令：%s\n", cmd.Args)
	//StdoutPipe方法返回一个在命令Start后与命令标准输出关联的管道。Wait方法获知命令结束后会关闭这个管道，一般不需要显式的关闭该管道。
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println("cmd.StdoutPipe: ", err)
		return err
	}
	cmd.Stderr = os.Stderr
	cmd.Dir = dir
	err = cmd.Start()
	if err != nil {
		return err
	}
	//创建一个流来读取管道内内容，这里逻辑是通过一行一行的读取的
	reader := bufio.NewReader(stdout)
	//实时循环读取输出流中的一行内容
	for {
		line, err2 := reader.ReadString('\n')
		if err2 != nil || io.EOF == err2 {
			break
		}
		fmt.Println(line)
	}
	err = cmd.Wait()
	return err
}

// 获取当前执行程序所在的绝对路径
func getCurrentAbPathByExecutable() string {
	exePath, err := os.Executable()
	if err != nil {
		log.Fatal(err)
	}
	res, _ := filepath.EvalSymlinks(filepath.Dir(exePath))
	return res
}
